home *** CD-ROM | disk | FTP | other *** search
/ Workbench Add-On / Workbench Add-On - Volume 1.iso / Text / Show / Less / less-252 / line.c < prev    next >
C/C++ Source or Header  |  1994-10-18  |  13KB  |  587 lines

  1. /*
  2.  * Copyright (c) 1984,1985,1989,1994  Mark Nudelman
  3.  * All rights reserved.
  4.  *
  5.  * Redistribution and use in source and binary forms, with or without
  6.  * modification, are permitted provided that the following conditions
  7.  * are met:
  8.  * 1. Redistributions of source code must retain the above copyright
  9.  *    notice, this list of conditions and the following disclaimer.
  10.  * 2. Redistributions in binary form must reproduce the above copyright
  11.  *    notice in the documentation and/or other materials provided with 
  12.  *    the distribution.
  13.  *
  14.  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY
  15.  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  16.  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 
  17.  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE
  18.  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
  19.  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT 
  20.  * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 
  21.  * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 
  22.  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 
  23.  * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN 
  24.  * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  25.  */
  26.  
  27.  
  28. /*
  29.  * Routines to manipulate the "line buffer".
  30.  * The line buffer holds a line of output as it is being built
  31.  * in preparation for output to the screen.
  32.  */
  33.  
  34. #include "less.h"
  35.  
  36. public char linebuf[1024];    /* Buffer which holds the current output line */
  37. public int size_linebuf = sizeof(linebuf);
  38.  
  39. static char attr[1024];        /* Extension of linebuf to hold attributes */
  40. static int curr;        /* Index into linebuf */
  41. static int column;        /* Printable length, accounting for
  42.                    backspaces, etc. */
  43. static int lno_indent;        /* Number of chars used for line number */
  44. static int overstrike;        /* Next char should overstrike previous char */
  45. static int is_null_line;    /* There is no current line */
  46. static char pendc;
  47.  
  48. static int do_append();
  49.  
  50. extern int bs_mode;
  51. extern int tabstop;
  52. extern int linenums;
  53. extern int ctldisp;
  54. extern int twiddle;
  55. extern int binattr;
  56. extern int auto_wrap, ignaw;
  57. extern int bo_s_width, bo_e_width;
  58. extern int ul_s_width, ul_e_width;
  59. extern int bl_s_width, bl_e_width;
  60. extern int so_s_width, so_e_width;
  61. extern int sc_width, sc_height;
  62.  
  63. /*
  64.  * Rewind the line buffer.
  65.  */
  66.     public void
  67. prewind()
  68. {
  69.     curr = 0;
  70.     column = 0;
  71.     overstrike = 0;
  72.     is_null_line = 0;
  73.     lno_indent = 0;
  74.     pendc = '\0';
  75. }
  76.  
  77. /*
  78.  * Insert the line number (of the given position) into the line buffer.
  79.  */
  80.     public void
  81. plinenum(pos)
  82.     POSITION pos;
  83. {
  84.     register int lno;
  85.     register int i;
  86.     register int n;
  87.  
  88.     /*
  89.      * We display the line number at the start of each line
  90.      * only if the -N option is set.
  91.      */
  92.     if (linenums != 2)
  93.         return;
  94.  
  95.     /*
  96.      * Get the line number and put it in the current line.
  97.      * {{ Note: since find_linenum calls forw_raw_line,
  98.      *    it may seek in the input file, requiring the caller 
  99.      *    of plinenum to re-seek if necessary. }}
  100.      */
  101.     lno = find_linenum(pos);
  102.  
  103.     sprintf(&linebuf[curr], "%6d", lno);
  104.     n = strlen(&linebuf[curr]);
  105.     column += n;
  106.     for (i = 0;  i < n;  i++)
  107.         attr[curr++] = 0;
  108.  
  109.     /*
  110.      * Append enough spaces to bring us to the next tab stop.
  111.      * {{ We could avoid this at the cost of adding some
  112.      *    complication to the tab stop logic in pappend(). }}
  113.      */
  114.     if (tabstop == 0)
  115.         tabstop = 1;
  116.     do
  117.     {
  118.         linebuf[curr] = ' ';
  119.         attr[curr++] = AT_NORMAL;
  120.         column++;
  121.     } while ((column % tabstop) != 0);
  122.     lno_indent = column;
  123. }
  124.  
  125. /*
  126.  * Return the printing width of the start (enter) sequence
  127.  * for a given character attribute.
  128.  */
  129.     int
  130. attr_swidth(a)
  131.     int a;
  132. {
  133.     switch (a)
  134.     {
  135.     case AT_BOLD:        return (bo_s_width);
  136.     case AT_UNDERLINE:    return (ul_s_width);
  137.     case AT_BLINK:        return (bl_s_width);
  138.     case AT_STANDOUT:    return (so_s_width);
  139.     }
  140.     return (0);
  141. }
  142.  
  143. /*
  144.  * Return the printing width of the end (exit) sequence
  145.  * for a given character attribute.
  146.  */
  147.     int
  148. attr_ewidth(a)
  149.     int a;
  150. {
  151.     switch (a)
  152.     {
  153.     case AT_BOLD:        return (bo_e_width);
  154.     case AT_UNDERLINE:    return (ul_e_width);
  155.     case AT_BLINK:        return (bl_e_width);
  156.     case AT_STANDOUT:    return (so_e_width);
  157.     }
  158.     return (0);
  159. }
  160.  
  161. /*
  162.  * Return the printing width of a given character and attribute,
  163.  * if the character were added to the current position in the line buffer.
  164.  * Adding a character with a given attribute may cause an enter or exit
  165.  * attribute sequence to be inserted, so this must be taken into account.
  166.  */
  167.     static int
  168. pwidth(c, a)
  169.     int c;
  170.     int a;
  171. {
  172.     register int w;
  173.  
  174.     if (c == '\b')
  175.         /*
  176.          * Backspace moves backwards one position.
  177.          */
  178.         return (-1);
  179.  
  180.     if (control_char(c))
  181.         /*
  182.          * Control characters do unpredicatable things,
  183.          * so we don't even try to guess; say it doesn't move.
  184.          * This can only happen if the -r flag is in effect.
  185.          */
  186.         return (0);
  187.  
  188.     /*
  189.      * Other characters take one space,
  190.      * plus the width of any attribute enter/exit sequence.
  191.      */
  192.     w = 1;
  193.     if (curr > 0 && attr[curr-1] != a)
  194.         w += attr_ewidth(attr[curr-1]);
  195.     if (a && (curr == 0 || attr[curr-1] != a))
  196.         w += attr_swidth(a);
  197.     return (w);
  198. }
  199.  
  200. /*
  201.  * Delete the previous character in the line buffer.
  202.  */
  203.     static void
  204. backc()
  205. {
  206.     curr--;
  207.     column -= pwidth(linebuf[curr], attr[curr]);
  208. }
  209.  
  210. /*
  211.  * Append a character and attribute to the line buffer.
  212.  */
  213.     static int
  214. storec(c, a)
  215.     int c;
  216.     int a;
  217. {
  218.     register int w;
  219.  
  220.     w = pwidth(c, a);
  221.     if (ctldisp > 0 && column + w + attr_ewidth(a) > sc_width)
  222.         /*
  223.          * Won't fit on screen.
  224.          */
  225.         return (1);
  226.  
  227.     if (curr >= sizeof(linebuf)-2)
  228.         /*
  229.          * Won't fit in line buffer.
  230.          */
  231.         return (1);
  232.  
  233.     /*
  234.      * Special handling for "magic cookie" terminals.
  235.      * If an attribute enter/exit sequence has a printing width > 0,
  236.      * and the sequence is adjacent to a space, delete the space.
  237.      * We just mark the space as invisible, to avoid having too
  238.      * many spaces deleted.
  239.      * {{ Note that even if the attribute width is > 1, we
  240.      *    delete only one space.  It's not worth trying to do more.
  241.      *    It's hardly worth doing this much. }}
  242.      */
  243.     if (curr > 0 && a != AT_NORMAL && 
  244.         linebuf[curr-1] == ' ' && attr[curr-1] == AT_NORMAL &&
  245.         attr_swidth(a) > 0)
  246.     {
  247.         /*
  248.          * We are about to append an enter-attribute sequence
  249.          * just after a space.  Delete the space.
  250.          */
  251.         attr[curr-1] = AT_INVIS;
  252.         column--;
  253.     } else if (curr > 0 && attr[curr-1] != AT_NORMAL && 
  254.         attr[curr-1] != AT_INVIS && c == ' ' && a == AT_NORMAL &&
  255.         attr_ewidth(attr[curr-1]) > 0)
  256.     {
  257.         /*
  258.          * We are about to append a space just after an 
  259.          * exit-attribute sequence.  Delete the space.
  260.          */
  261.         a = AT_INVIS;
  262.         column--;
  263.     }
  264.     /* End of magic cookie handling. */
  265.  
  266.     linebuf[curr] = c;
  267.     attr[curr] = a;
  268.     column += w;
  269.     return (0);
  270. }
  271.  
  272. /*
  273.  * Append a character to the line buffer.
  274.  * Expand tabs into spaces, handle underlining, boldfacing, etc.
  275.  * Returns 0 if ok, 1 if couldn't fit in buffer.
  276.  */
  277.     public int
  278. pappend(c)
  279.     register int c;
  280. {
  281.     if (pendc)
  282.     {
  283.         if (do_append(pendc))
  284.             /*
  285.              * Oops.  We've probably lost the char which
  286.              * was in pendc, since caller won't back up.
  287.              */
  288.             return (1);
  289.         pendc = '\0';
  290.     }
  291.  
  292.     if (c == '\r' && bs_mode == BS_SPECIAL)
  293.     {
  294.         /*
  295.          * Don't put the CR into the buffer until we see 
  296.          * the next char.  If the next char is a newline,
  297.          * discard the CR.
  298.          */
  299.         pendc = c;
  300.         return (0);
  301.     }
  302.  
  303.     return (do_append(c));
  304. }
  305.  
  306.     static int
  307. do_append(c)
  308.     int c;
  309. {
  310.     register char *s;
  311.     register int a;
  312.  
  313. #define    STOREC(c,a)    if (storec((c),(a))) return (1); else curr++
  314.  
  315.     if (overstrike)
  316.     {
  317.         /*
  318.          * Overstrike the character at the current position
  319.          * in the line buffer.  This will cause either 
  320.          * underline (if a "_" is overstruck), 
  321.          * bold (if an identical character is overstruck),
  322.          * or just deletion of the character in the buffer.
  323.          */
  324.         overstrike = 0;
  325.         if ((char)c == linebuf[curr])
  326.             STOREC(linebuf[curr], AT_BOLD);
  327.         else if (c == '_')
  328.             STOREC(linebuf[curr], AT_UNDERLINE);
  329.         else if (linebuf[curr] == '_')
  330.             STOREC(c, AT_UNDERLINE);
  331.         else if (control_char(c))
  332.             goto do_control_char;
  333.         else
  334.             STOREC(c, AT_NORMAL);
  335.     } else if (c == '\b')
  336.     {
  337.         switch (bs_mode)
  338.         {
  339.         case BS_NORMAL:
  340.             STOREC(c, AT_NORMAL);
  341.             break;
  342.         case BS_CONTROL:
  343.             goto do_control_char;
  344.         case BS_SPECIAL:
  345.             if (curr == 0)
  346.                 break;
  347.             backc();
  348.             overstrike = 1;
  349.             break;
  350.         }
  351.     } else if (c == '\t') 
  352.     {
  353.         /*
  354.          * Expand a tab into spaces.
  355.          */
  356.         if (tabstop == 0)
  357.             tabstop = 1;
  358.         do
  359.         {
  360.             STOREC(' ', AT_NORMAL);
  361.         } while ((column % tabstop) != 0);
  362.     } else if (control_char(c))
  363.     {
  364.     do_control_char:
  365.         if (ctldisp == 0)
  366.         {
  367.             /*
  368.              * Output as a normal character.
  369.              */
  370.             STOREC(c, AT_NORMAL);
  371.         } else 
  372.         {
  373.             /*
  374.              * Convert to printable representation.
  375.              */
  376.             s = prchar(c);  
  377.             a = binattr;
  378.  
  379.             /*
  380.              * Make sure we can get the entire representation
  381.              * of the character on this line.
  382.              */
  383.             if (column + strlen(s) + 
  384.                 attr_swidth(a) + attr_ewidth(a) > sc_width)
  385.                 return (1);
  386.  
  387.             for ( ;  *s != 0;  s++)
  388.                 STOREC(*s, a);
  389.         }
  390.     } else
  391.     {
  392.         STOREC(c, AT_NORMAL);
  393.     }
  394.  
  395.     return (0);
  396. }
  397.  
  398. /*
  399.  * Terminate the line in the line buffer.
  400.  */
  401.     public void
  402. pdone(endline)
  403.     int endline;
  404. {
  405.     if (pendc && (pendc != '\r' || !endline))
  406.         /*
  407.          * If we had a pending character, put it in the buffer.
  408.          * But discard a pending CR if we are at end of line
  409.          * (that is, discard the CR in a CR/LF sequence).
  410.          */
  411.         (void) do_append(pendc);
  412.  
  413. #if HILITE_SEARCH
  414.     /*
  415.      * Modify the attribute for matched strings; do this before we
  416.      * add a newline so that '$' will match end of line properly.
  417.      * {{ If a matched string is broken at the end of the line,
  418.      *    we won't mark it.  This is difficlt to fix. }}
  419.      */
  420.     {
  421.         extern int hilite_search;
  422.         if (hilite_search)
  423.         {
  424.             linebuf[curr] = '\0';
  425.             hlsearch(linebuf + lno_indent, attr + lno_indent, 
  426.                 AT_STANDOUT);
  427.         }
  428.     }
  429. #endif
  430.   
  431.     /*
  432.      * Add a newline if necessary,
  433.      * and append a '\0' to the end of the line.
  434.      */
  435.     if (column < sc_width || !auto_wrap || ignaw || ctldisp == 0)
  436.     {
  437.         linebuf[curr] = '\n';
  438.         attr[curr] = AT_NORMAL;
  439.         curr++;
  440.     }
  441.     linebuf[curr] = '\0';
  442.     attr[curr] = AT_NORMAL;
  443. }
  444.  
  445. /*
  446.  * Get a character from the current line.
  447.  * Return the character as the function return value,
  448.  * and the character attribute in *ap.
  449.  */
  450.     public int
  451. gline(i, ap)
  452.     register int i;
  453.     register int *ap;
  454. {
  455.     char *s;
  456.     
  457.     if (is_null_line)
  458.     {
  459.         /*
  460.          * If there is no current line, we pretend the line is
  461.          * either "~" or "", depending on the "twiddle" flag.
  462.          */
  463.         *ap = AT_NORMAL;
  464.         s = (twiddle) ? "~\n" : "\n";
  465.         return (s[i]);
  466.     }
  467.  
  468.     *ap = attr[i];
  469.     return (linebuf[i] & 0377);
  470. }
  471.  
  472. /*
  473.  * Indicate that there is no current line.
  474.  */
  475.     public void
  476. null_line()
  477. {
  478.     is_null_line = 1;
  479. }
  480.  
  481. #if 1
  482. /*
  483.  * Analogous to forw_line(), but deals with "raw lines":
  484.  * lines which are not split for screen width.
  485.  * {{ This is supposed to be more efficient than forw_line(). }}
  486.  */
  487.     public POSITION
  488. forw_raw_line(curr_pos, linep)
  489.     POSITION curr_pos;
  490.     char **linep;
  491. {
  492.     register char *p;
  493.     register int c;
  494.     POSITION new_pos;
  495.  
  496.     if (curr_pos == NULL_POSITION || ch_seek(curr_pos) ||
  497.         (c = ch_forw_get()) == EOI)
  498.         return (NULL_POSITION);
  499.  
  500.     p = linebuf;
  501.  
  502.     for (;;)
  503.     {
  504.         if (c == '\n' || c == EOI)
  505.         {
  506.             new_pos = ch_tell();
  507.             break;
  508.         }
  509.         if (p >= &linebuf[sizeof(linebuf)-1])
  510.         {
  511.             /*
  512.              * Overflowed the input buffer.
  513.              * Pretend the line ended here.
  514.              * {{ The line buffer is supposed to be big
  515.              *    enough that this never happens. }}
  516.              */
  517.             new_pos = ch_tell() - 1;
  518.             break;
  519.         }
  520.         *p++ = c;
  521.         c = ch_forw_get();
  522.     }
  523.     *p = '\0';
  524.     if (linep != NULL)
  525.         *linep = linebuf;
  526.     return (new_pos);
  527. }
  528.  
  529. /*
  530.  * Analogous to back_line(), but deals with "raw lines".
  531.  * {{ This is supposed to be more efficient than back_line(). }}
  532.  */
  533.     public POSITION
  534. back_raw_line(curr_pos, linep)
  535.     POSITION curr_pos;
  536.     char **linep;
  537. {
  538.     register char *p;
  539.     register int c;
  540.     POSITION new_pos;
  541.  
  542.     if (curr_pos == NULL_POSITION || curr_pos <= ch_zero() ||
  543.         ch_seek(curr_pos-1))
  544.         return (NULL_POSITION);
  545.  
  546.     p = &linebuf[sizeof(linebuf)];
  547.     *--p = '\0';
  548.  
  549.     for (;;)
  550.     {
  551.         c = ch_back_get();
  552.         if (c == '\n')
  553.         {
  554.             /*
  555.              * This is the newline ending the previous line.
  556.              * We have hit the beginning of the line.
  557.              */
  558.             new_pos = ch_tell() + 1;
  559.             break;
  560.         }
  561.         if (c == EOI)
  562.         {
  563.             /*
  564.              * We have hit the beginning of the file.
  565.              * This must be the first line in the file.
  566.              * This must, of course, be the beginning of the line.
  567.              */
  568.             new_pos = ch_zero();
  569.             break;
  570.         }
  571.         if (p <= linebuf)
  572.         {
  573.             /*
  574.              * Overflowed the input buffer.
  575.              * Pretend the line ended here.
  576.              */
  577.             new_pos = ch_tell() + 1;
  578.             break;
  579.         }
  580.         *--p = c;
  581.     }
  582.     if (linep != NULL)
  583.         *linep = p;
  584.     return (new_pos);
  585. }
  586. #endif
  587.